; spinlock.asm
; SMP safe spinlock implementation
;
;  Copyright 2005 Corey Tabaka
;
;  Licensed under the Apache License, Version 2.0 (the "License");
;  you may not use this file except in compliance with the License.
;  You may obtain a copy of the License at
;
;      http://www.apache.org/licenses/LICENSE-2.0
;
;  Unless required by applicable law or agreed to in writing, software
;  distributed under the License is distributed on an "AS IS" BASIS,
;  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;  See the License for the specific language governing permissions and
;  limitations under the License.
;

; These could probably benefit from GCC inlining, but the stable symbols are
; more important for debugging and verification right now.

section .text
bits 32

; void __attribute__ ((regparm(1)) spinlock_init(spinlock_t* ptr);
; int __attribute__ ((regparm(1)) spinlock_wait(spinlock_t* ptr);
; void __attribute__ ((regparm(1)) spinlock_signal(spinlock_t* ptr);
; int __attribute__ ((regparm(1)) spinlock_try(spinlock_t* ptr);

[global _spinlock_init]
[global _spinlock_wait]
[global _spinlock_signal]
[global _spinlock_try]

_spinlock_init:
	mov dword [eax],0
	
	ret

_spinlock_wait:
	mov edx,eax					; get pointer to mutex and free up eax

.spin:
	cmp dword [edx],0			; check if lock is free (avoid too many bus locks)
	je .getlock
	pause
	jmp .spin
	
.getlock:	
	mov eax,1
	xchg eax,[edx]				; swap the mutex value with 1 (only one proc can do this at a time)
	cmp eax,0					; see if the mutex was locked
	jne .spin					; spin until we get a zero (previously unlocked) in eax
	
	mov eax,0
	ret

_spinlock_signal:
	mov dword [eax],0			; unlock mutex
	
	ret

_spinlock_try:
	mov edx,eax					; get pointer to mutex and free up eax

	cmp dword [edx],0			; check if lock is free
	je .getlock
	
	mov eax,1					; return failure
	jmp .end
	
.getlock:	
	mov eax,1
	xchg eax,[edx]				; swap the mutex value with 1 (only one proc can do this at a time)
	
.end:
	ret
	
; void __attribute__ ((regparm(1))) interrupt_mutex_wait(int_mutex_t *);
[global _interrupt_mutex_wait]

_interrupt_mutex_wait:
	pushfd						; get the flags
	pop edx
	and edx,0x200				; we only care about IF
	mov [eax + 4],edx			; store IF in the structure
	cli							; clear interrupts
	
	; the spinlock is the first element of the int_mutex_t pointed to
	; the param in eax, so spinlock_wait can find it
	call _spinlock_wait			; lock
	
	ret

; void __attribute__ ((regparm(1))) interrupt_mutex_signal(int_mutex_t *);
[global _interrupt_mutex_signal]

_interrupt_mutex_signal:
	call _spinlock_signal
	
	pushfd						; get the flags
	pop edx
	or edx,[eax + 4]			; copy back the saved IF
	push edx					; put the flags back
	popfd
	
	ret

[global _interrupt_mutex_init]

_interrupt_mutex_init:
	call _spinlock_init
	
	ret
	